import 'reflect-metadata'
import * as express from 'express'
import * as PATH from 'path'
import * as FS from 'fs'
import * as YAML from 'yamljs'
import { JSONResponse } from '../utils'
type ParamType = 'query'|'body'|'path'|'request'|'response'




var detectPlatform = function () {
    switch (process.platform) {
      case "darwin": //unix 系统内核
        console.log('darwin ');
        return 'darwin'

      case "win32": //windows 系统内核
      return 'win'
   
    }
    return 'darwin'
  };
  



type ParamDecoratorType = {
    name:string
    index:number
    type:ParamType,
    defaultValue:any
}

let routers = {}

let sub_routers = {
    'get':[],
    'post':[],
    'put':[],
    'delete':[],
    'patch':[]
}
let controllerPath = ''

/**
 * 控制器缓存池
 */
let controller_cache = {}

/**
 * 配置缓存池
 */
let config_cache = {}


type ControllerDecorator = (path:string)=>any
type MethodDecorator = (...args)=>any

type ParamDecorator = (...args)=>any

/**
 * 控制器
 * @param path 
 */
export const Controller:ControllerDecorator = (path:string)=>{
    return func => {
        if (!routers[path]) {
            const route = express.Router()
            routers[path] = {
                controller:func.name,
                route
            }
        }
        return function(...args){
            return new func(args)
        }
    }
}


export const RestController:ControllerDecorator = (path)=>{
    return func => {
        if (!routers[path]) {
            const route = express.Router()
            routers[path] = {
                controller:func.name,
                route
            }
        }
        return function(...args){
            return new func(args)
        }
    }
}

/**
 * get请求
 * @param path 请求路径
 */
export const GET:MethodDecorator = (path:string,response = false)=>{
    return function (target: any, methodName: string, config: any) {
        sub_routers.get.push({
            path,
            controller:target.constructor,
            method:methodName,
            response
        })
    }
}

export const POST:MethodDecorator = (path:string,response = false)=>{
    return function (target: any, methodName: string, config: any) {
        sub_routers.post.push({
            path,
            controller:target.constructor,
            method:methodName,
            response
        })
    }
}

export const PUT:MethodDecorator = (path:string,response = false)=>{
    return function (target: any, methodName: string, config: any) {
        sub_routers.put.push({
            path,
            controller:target.constructor,
            method:methodName,
            response
        })
    }
}

export const DELETE:MethodDecorator = (path:string,response = false)=>{
    return function (target: any, methodName: string, config: any) {
        sub_routers.delete.push({
            path,
            controller:target.constructor,
            method:methodName,
            response
        })
    }
}

export const PATCH:MethodDecorator = (path:string,response = false)=>{
    return function (target: any, methodName: string, config: any) {
        sub_routers.patch.push({
            path,
            controller:target.constructor,
            method:methodName,
            response
        })
    }
}





/**
 * 
 * @param server express应用实例
 * @param controller_path 控制器目录绝对路径
 */
export function generateRouter(server,controller_path){
    controllerPath = controllerPath;
    scanDir(controller_path)
    for(let method_name in sub_routers){
        const methods = sub_routers[method_name]
        methods.forEach(method => {
            for(let router_name in routers){
                const router = routers[router_name]
                if(router.controller == method.controller.name){
                    router.route[method_name](method.path,async function(req,res){
                        let inject_params_instance = []

                        try {
                            const inject_params = Reflect.getMetadata('design:paramtypes',method.controller)
                            Array.isArray(inject_params) && inject_params.forEach(i=>inject_params_instance.push(new i()))
                        } catch (error) {}
                        
                        if(!controller_cache[router.controller]){
                            const controller = new method.controller(...inject_params_instance)
                           
                            controller_cache[router.controller] = controller
                        }
                        const controller = controller_cache[router.controller]
            
                        const params_d:ParamDecoratorType[] = Reflect.getMetadata('design:paramname',controller[method.method])
                        let invoke_params = []
                        if(Array.isArray(params_d)){
                            params_d.sort((i,j)=>i.index>j.index?1:-1).forEach(p=>{
                                if(p.type == 'query'){
                                    // let v = (req.query || {})['getValueForKeyPath'](p.name) || p.defaultValue
                                    let v = Object.prototype['getValueForKeyPath'].call(req.query || {},p.name)|| p.defaultValue
                                    if(!p.name){
                                        v = req.query|| p.defaultValue
                                    }
                                    invoke_params.push(v)
                                }
                                else if(p.type == 'path'){
                                    let v = Object.prototype['getValueForKeyPath'].call(req.params || {},p.name)|| p.defaultValue
                                    if(!p.name){
                                        v = req.params|| p.defaultValue
                                    }
                                    invoke_params.push(v)
                                }else if(p.type == 'body'){
                                    let v = null
                                    if(req.body){
                                        let v = Object.prototype['getValueForKeyPath'].call(req.body || {},p.name)|| p.defaultValue
                                        if(!p.name){
                                            v = req.body|| p.defaultValue
                                        }
                                    }
                                    invoke_params.push(v)
                                }else if(p.type == 'request'){
                                    invoke_params.push(req)
                                }
                                else if(p.type == 'response'){
                                    invoke_params.push(res)
                                }
                                else{
                                    invoke_params.push(null)
                                }
                            })
                        }
                        const invoke_method = controller[method.method]
                        try {
                            var value = await invoke_method.apply(controller,invoke_params)
                            if(!method.response){
                                res.json(JSONResponse.success(value))
                            }
                           
                           
                        } catch (error) {
                            res.json(JSONResponse.error(error))
                        }
                   })
                }
            }
        });
    }
    
    for (let path in routers){
        server.use(path,routers[path].route)
    }
    
}

function getFileName(data) {
    return data.substring(0,data.indexOf("."));
}

function readFileList(dir, filesList = []) {
      const files = FS.readdirSync(dir);
      files.forEach((item, index) => {
        var fullPath = PATH.join(dir, item);
        const stat = FS.statSync(fullPath);
        if (stat.isDirectory()) {   
          readFileList(PATH.join(dir, item), filesList); //递归读取文件
        } else {        
          filesList.push(getFileName(fullPath));           
        }    
      });
      return filesList;
}


/**
 * 扫描控制器目录并生成入口文件
 * @param dir 
 */
function scanDir(dir){
    console.log('扫描控制器目录中...')
    const f = readFileList(dir)
    console.log('扫描控制器目录结束')


    const _target = PATH.join(__dirname,'router.js')
    let content = ''
    f.forEach(i=>{
        if(detectPlatform() == 'win'){
            content+=`require('${i.replace(/\\/g,'\\\\')}');\n`
        }else{
            content+=`require('${i}');\n`
        }
       
    })

    if(FS.existsSync(_target)){
        const c = FS.readFileSync(_target,'utf-8') 
        if(c == content){
            console.log('路由配置文件已存在')
            require('./router')
        }else{
            console.log('路由配置文件已存在,控制器文件存在修改。更新中...')
            FS.writeFileSync(_target,content,'utf-8')
            console.log('路由配置文件已存在,控制器文件更新完成..')
            require('./router')
        }
    }else{
        console.log('路由配置文件不存在,创建中...')
        FS.writeFileSync(_target,content,'utf-8')
        console.log('路由配置创建完成...')
        require('./router')
    }
}


function params_decorator(target,key,index,name,type,defaultValue){
    let value = Reflect.getMetadata('design:paramname',target[key])
    if(!value){
        value = []
    }
    value.push({
        name,
        index,
        type,
        defaultValue
    })
   Reflect.defineMetadata('design:paramname',value,target[key])
}



/**
 * get or delete请求的参数
 * 
 * @param args 
 * @example
 * // returns 2
 * get(@Params('name') name)
 */
export const Params:ParamDecorator=(...args)=>{
    if(args.length > 0 && typeof args[0] == 'string' && args[0]){
        return function(...args1){
            params_decorator.apply(null,[...args1,args[0],'query',args[1]])
        }
    }else{
        params_decorator.apply(null,[...args,null,'query'])
    }
}

export const Path:ParamDecorator = (...args)=>{
    if(args.length > 0 && typeof args[0] == 'string' && args[0]){
        return function(...args1){
            params_decorator.apply(null,[...args1,args[0],'path',args[1]])
        }
    }else{
        params_decorator.apply(null,[...args,null,'path'])
    }
}

/**
 * post or put 请求参数的body
 * @param args 
 */
export const Body:ParamDecorator = (...args)=>{
    if(args.length > 0 && typeof args[0] == 'string' && args[0]){
        return function(...args1){
            params_decorator.apply(null,[...args1,args[0],'body',args[1]])
        }
    }else{
        params_decorator.apply(null,[...args,null,'body'])
    }
}


/**
 * 原始请求对象
 * @param args 
 */
export const Request:ParamDecorator = (...args)=>{
    params_decorator.apply(null,[...args,null,'request'])
}

/**
 * 原始响应对象
 */
export const Response:ParamDecorator = (...args)=>{
    params_decorator.apply(null,[...args,null,'response'])
}


export const Configure:ParamDecorator = (file:string,path:string)=>{
    var fullPath = PATH.join(process.cwd(),file + '.yml')
    let data = config_cache[fullPath]
    if(!data){
        try{
            data = YAML.parse(FS.readFileSync(fullPath).toString());
            config_cache[fullPath] = data
        }catch(e){
            data = {}
        }
    }
    return function(target,key){
        const descriptor:PropertyDescriptor = {
            set:(v)=>{

            },
            get:()=>{
                return data['getValueForKeyPath'](path)
            }
        }
        return descriptor
    }
}


export const clean = ()=>{
    console.log('清除缓存文件')
    const _target = PATH.join(__dirname,'router.js')
    if(FS.existsSync(_target)){
        FS.unlinkSync(_target)
    }
}